using System.Threading;
using System.Windows.Controls;
using Microsoft.Practices.CompositeUI;
using Microsoft.Practices.CompositeUI.SmartParts;
using Microsoft.Practices.ObjectBuilder;
using NUnit.Framework;
using Rhino.Mocks;
using SCSFContrib.CompositeUI.WPF.BuilderStrategies;

namespace SCSFContrib.CompositeUI.WPF.UnitTest.BuilderStrategies
{
	[TestFixture]
	public sealed class FrameworkElementSmartPartStrategyTest : UnitTest
	{
		private FrameworkElementSmartPartStrategy _frameworkElementSmartPartStrategy;
		private TestableRootWorkItem _workItem;

		protected override void SetUpCore()
		{
			base.SetUpCore();
			_frameworkElementSmartPartStrategy = new FrameworkElementSmartPartStrategy();
			_workItem = new TestableRootWorkItem();
		}

		[Test]
		public void BuildUp_ShouldIgnoreAnyNonFrameworkElementArguments()
		{
			IBuilderContext context = Mocks.DynamicMock<IBuilderContext>();
			Mocks.ReplayAll();

			Assert.AreEqual(1, _workItem.Items.Count);
			_frameworkElementSmartPartStrategy.BuildUp(context, null, "abc", null);
			_frameworkElementSmartPartStrategy.BuildUp(context, null, 123, null);
			_frameworkElementSmartPartStrategy.BuildUp(context, null, this, null);
			_frameworkElementSmartPartStrategy.BuildUp(context, null, _frameworkElementSmartPartStrategy, null);
			Assert.AreEqual(1, _workItem.Items.Count);
		}

		[Test]
		public void BuildUp_ShouldAddChildSmartPartsToWorkItem()
		{
			IBuilderContext context = Mocks.DynamicMock<IBuilderContext>();
			IReadWriteLocator locator = Mocks.DynamicMultiMock<IReadWriteLocator>();
			Expect.Call(context.Locator).Repeat.Any().Return(locator);
			Expect.Call(locator.Get<WorkItem>(null)).IgnoreArguments().Repeat.Once().Return(_workItem);
			Mocks.ReplayAll();

			StackPanel stackPanel = new StackPanel();
			stackPanel.Children.Add(new Button());
			stackPanel.Children.Add(new TextBox());
			stackPanel.Children.Add(new MockSmartPart());
			MockSmartPart smartPart = new MockSmartPart();
			smartPart.Content = stackPanel;

			Assert.AreEqual(1, _workItem.Items.Count);
			_frameworkElementSmartPartStrategy.BuildUp(context, null, smartPart, null);
			Assert.AreEqual(2, _workItem.Items.Count);
			Assert.IsFalse(_workItem.Items.ContainsObject(smartPart));
			Assert.IsFalse(_workItem.Items.ContainsObject(stackPanel.Children[0]));
			Assert.IsFalse(_workItem.Items.ContainsObject(stackPanel.Children[1]));
			Assert.IsTrue(_workItem.Items.ContainsObject(stackPanel.Children[2]));
		}

		[Test]
		public void BuildUp_ShouldAddSmartPartPlaceholdersToWorkItem()
		{
			IBuilderContext context = Mocks.DynamicMock<IBuilderContext>();
			IReadWriteLocator locator = Mocks.DynamicMultiMock<IReadWriteLocator>();
			Expect.Call(context.Locator).Repeat.Any().Return(locator);
			Expect.Call(locator.Get<WorkItem>(null)).IgnoreArguments().Repeat.Once().Return(_workItem);
			Mocks.ReplayAll();

			ContentControl contentControl1 = new ContentControl();
			ContentControl contentControl2 = new ContentControl();
			contentControl1.Content = Mocks.DynamicMock<ISmartPartPlaceholder>();
			contentControl2.Content = Mocks.DynamicMock<ISmartPartPlaceholder>();

			StackPanel stackPanel = new StackPanel();
			stackPanel.Children.Add(new Button());
			stackPanel.Children.Add(contentControl1);
			stackPanel.Children.Add(new TextBox());
			stackPanel.Children.Add(contentControl2);

			Assert.AreEqual(1, _workItem.Items.Count);
			_frameworkElementSmartPartStrategy.BuildUp(context, null, stackPanel, null);
			Assert.AreEqual(3, _workItem.Items.Count);
			Assert.IsFalse(_workItem.Items.ContainsObject(stackPanel.Children[0]));
			Assert.IsTrue(_workItem.Items.ContainsObject(contentControl1.Content));
			Assert.IsFalse(_workItem.Items.ContainsObject(stackPanel.Children[2]));
			Assert.IsTrue(_workItem.Items.ContainsObject(contentControl2.Content));
		}

		[Test]
		public void BuildUp_ShouldAddWorkspacesToWorkItem()
		{
			IBuilderContext context = Mocks.DynamicMock<IBuilderContext>();
			IReadWriteLocator locator = Mocks.DynamicMultiMock<IReadWriteLocator>();
			Expect.Call(context.Locator).Repeat.Any().Return(locator);
			Expect.Call(locator.Get<WorkItem>(null)).IgnoreArguments().Repeat.Once().Return(_workItem);
			Mocks.ReplayAll();

			ContentControl contentControl1 = new ContentControl();
			ContentControl contentControl2 = new ContentControl();
			contentControl1.Content = Mocks.DynamicMock<IWorkspace>();
			contentControl2.Content = Mocks.DynamicMock<IWorkspace>();

			StackPanel stackPanel = new StackPanel();
			stackPanel.Children.Add(new Button());
			stackPanel.Children.Add(contentControl1);
			stackPanel.Children.Add(new TextBox());
			stackPanel.Children.Add(contentControl2);

			Assert.AreEqual(1, _workItem.Items.Count);
			_frameworkElementSmartPartStrategy.BuildUp(context, null, stackPanel, null);
			Assert.AreEqual(3, _workItem.Items.Count);
			Assert.IsFalse(_workItem.Items.ContainsObject(stackPanel.Children[0]));
			Assert.IsTrue(_workItem.Items.ContainsObject(contentControl1.Content));
			Assert.IsFalse(_workItem.Items.ContainsObject(stackPanel.Children[2]));
			Assert.IsTrue(_workItem.Items.ContainsObject(contentControl2.Content));
		}

		[Test]
		public void BuildUp_ShouldPopulateRootPlaceholderWherePossible()
		{
			MockSmartPart smartPart1 = _workItem.SmartParts.AddNew<MockSmartPart>("Name");
			MockSmartPart smartPart2 = new MockSmartPart();

			IBuilderContext context = Mocks.DynamicMock<IBuilderContext>();
			IReadWriteLocator locator = Mocks.DynamicMultiMock<IReadWriteLocator>();
			Expect.Call(context.Locator).Repeat.Any().Return(locator);
			Expect.Call(locator.Get<WorkItem>(null)).IgnoreArguments().Repeat.Once().Return(_workItem);
			ISmartPartPlaceholder rootPlaceholder = Mocks.DynamicMultiMock(typeof(MockSmartPart), typeof(ISmartPartPlaceholder)) as ISmartPartPlaceholder;
			Expect.Call(rootPlaceholder.SmartPartName).Repeat.Any().Return("Name");
			rootPlaceholder.SmartPart = smartPart1;
			LastCall.Repeat.Once();
			ISmartPartPlaceholder childPlaceholder = Mocks.DynamicMultiMock(typeof(MockSmartPart), typeof(ISmartPartPlaceholder)) as ISmartPartPlaceholder;
			Expect.Call(childPlaceholder.SmartPartName).Repeat.Any().Return("NonExistantName");
			childPlaceholder.SmartPart = smartPart2;
			LastCall.Repeat.Never();
			Mocks.ReplayAll();

			(rootPlaceholder as MockSmartPart).Content = childPlaceholder;
			Assert.AreEqual(2, _workItem.Items.Count);
			Assert.IsFalse(_workItem.Items.ContainsObject(childPlaceholder));
			_frameworkElementSmartPartStrategy.BuildUp(context, null, rootPlaceholder, null);
		}

		[Test]
		public void BuildUp_ShouldAddItemsUnderTheirName()
		{
			IBuilderContext context = Mocks.DynamicMock<IBuilderContext>();
			IReadWriteLocator locator = Mocks.DynamicMultiMock<IReadWriteLocator>();
			Expect.Call(context.Locator).Repeat.Any().Return(locator);
			Expect.Call(locator.Get<WorkItem>(null)).IgnoreArguments().Repeat.Once().Return(_workItem);
			Mocks.ReplayAll();

			MockSmartPart smartPart1 = new MockSmartPart();
			smartPart1.Name = "smartPart";
			MockSmartPart smartPart2 = new MockSmartPart();
			smartPart2.Name = "smartPart2";
			MockSmartPart smartPart3 = new MockSmartPart();
			smartPart3.Name = "";
			MockSmartPart smartPart4 = new MockSmartPart();
			smartPart3.Name = null;
			StackPanel stackPanel = new StackPanel();
			stackPanel.Children.Add(smartPart1);
			stackPanel.Children.Add(smartPart2);
			stackPanel.Children.Add(smartPart3);
			stackPanel.Children.Add(smartPart4);

			Assert.AreEqual(1, _workItem.Items.Count);
			_frameworkElementSmartPartStrategy.BuildUp(context, null, stackPanel, null);
			Assert.AreEqual(5, _workItem.Items.Count);
			Assert.IsTrue(_workItem.Items.ContainsObject(smartPart1));
			Assert.IsTrue(_workItem.Items.ContainsObject(smartPart2));
			Assert.IsTrue(_workItem.Items.ContainsObject(smartPart3));
			Assert.IsTrue(_workItem.Items.ContainsObject(smartPart4));
			Assert.AreSame(smartPart1, _workItem.Items.Get("smartPart"));
			Assert.AreSame(smartPart2, _workItem.Items.Get("smartPart2"));
			Assert.IsNull(_workItem.Items.Get(""));
		}

		[Test]
		public void TearDown_ShouldIgnoreAnyNonFrameworkElementArguments()
		{
			IBuilderContext context = Mocks.DynamicMock<IBuilderContext>();
			Mocks.ReplayAll();

			Assert.AreEqual(1, _workItem.Items.Count);
			_frameworkElementSmartPartStrategy.TearDown(context, "abc");
			_frameworkElementSmartPartStrategy.TearDown(context, 123);
			_frameworkElementSmartPartStrategy.TearDown(context, this);
			_frameworkElementSmartPartStrategy.TearDown(context, _frameworkElementSmartPartStrategy);
			Assert.AreEqual(1, _workItem.Items.Count);
		}

		[Test]
		public void TearDown_ShouldRemoveAnySmartPartsFromTheWorkItem()
		{
			IBuilderContext context = Mocks.DynamicMock<IBuilderContext>();
			IReadWriteLocator locator = Mocks.DynamicMultiMock<IReadWriteLocator>();
			Expect.Call(context.Locator).Repeat.Any().Return(locator);
			Expect.Call(locator.Get<WorkItem>(null)).IgnoreArguments().Repeat.Once().Return(_workItem);
			Mocks.ReplayAll();

			MockSmartPart smartPart1 = _workItem.Items.AddNew<MockSmartPart>();
			MockSmartPart smartPart2 = _workItem.Items.AddNew<MockSmartPart>();
			DockPanel dockPanel = new DockPanel();
			dockPanel.Children.Add(smartPart2);
			StackPanel stackPanel = new StackPanel();
			stackPanel.Children.Add(new TextBox());
			stackPanel.Children.Add(smartPart1);
			stackPanel.Children.Add(new Button());
			stackPanel.Children.Add(dockPanel);

			Assert.AreEqual(3, _workItem.Items.Count);
			Assert.IsTrue(_workItem.Items.ContainsObject(smartPart1));
			Assert.IsTrue(_workItem.Items.ContainsObject(smartPart2));
			_frameworkElementSmartPartStrategy.TearDown(context, stackPanel);
			Assert.AreEqual(1, _workItem.Items.Count);
			Assert.IsFalse(_workItem.Items.ContainsObject(smartPart1));
			Assert.IsFalse(_workItem.Items.ContainsObject(smartPart2));
		}

		[Test]
		public void TearDown_ShouldRemoveAnySmartPartPlaceholdersFromTheWorkItem()
		{
			IBuilderContext context = Mocks.DynamicMock<IBuilderContext>();
			IReadWriteLocator locator = Mocks.DynamicMultiMock<IReadWriteLocator>();
			Expect.Call(context.Locator).Repeat.Any().Return(locator);
			Expect.Call(locator.Get<WorkItem>(null)).IgnoreArguments().Repeat.Once().Return(_workItem);
			ISmartPartPlaceholder placeholder1 = Mocks.DynamicMultiMock(typeof(MockSmartPart), typeof(ISmartPartPlaceholder)) as ISmartPartPlaceholder;
			ISmartPartPlaceholder placeholder2 = Mocks.DynamicMultiMock(typeof(MockSmartPart), typeof(ISmartPartPlaceholder)) as ISmartPartPlaceholder;
			Mocks.ReplayAll();

			_workItem.Items.Add(placeholder1);
			_workItem.Items.Add(placeholder2);

			DockPanel dockPanel = new DockPanel();
			dockPanel.Children.Add(placeholder2 as MockSmartPart);
			StackPanel stackPanel = new StackPanel();
			stackPanel.Children.Add(new TextBox());
			stackPanel.Children.Add(placeholder1 as MockSmartPart);
			stackPanel.Children.Add(new Button());
			stackPanel.Children.Add(dockPanel);

			Assert.AreEqual(3, _workItem.Items.Count);
			Assert.IsTrue(_workItem.Items.ContainsObject(placeholder1));
			Assert.IsTrue(_workItem.Items.ContainsObject(placeholder2));
			_frameworkElementSmartPartStrategy.TearDown(context, stackPanel);
			Assert.AreEqual(1, _workItem.Items.Count);
			Assert.IsFalse(_workItem.Items.ContainsObject(placeholder1));
			Assert.IsFalse(_workItem.Items.ContainsObject(placeholder2));
		}

		[Test]
		public void TearDown_ShouldRemoveAnyWorkspacesFromTheWorkItem()
		{
			IBuilderContext context = Mocks.DynamicMock<IBuilderContext>();
			IReadWriteLocator locator = Mocks.DynamicMultiMock<IReadWriteLocator>();
			Expect.Call(context.Locator).Repeat.Any().Return(locator);
			Expect.Call(locator.Get<WorkItem>(null)).IgnoreArguments().Repeat.Once().Return(_workItem);
			IWorkspace workspace1 = Mocks.DynamicMultiMock(typeof(MockSmartPart), typeof(IWorkspace)) as IWorkspace;
			IWorkspace workspace2 = Mocks.DynamicMultiMock(typeof(MockSmartPart), typeof(IWorkspace)) as IWorkspace;
			Mocks.ReplayAll();

			_workItem.Items.Add(workspace1);
			_workItem.Items.Add(workspace2);

			DockPanel dockPanel = new DockPanel();
			dockPanel.Children.Add(workspace2 as MockSmartPart);
			StackPanel stackPanel = new StackPanel();
			stackPanel.Children.Add(new TextBox());
			stackPanel.Children.Add(workspace1 as MockSmartPart);
			stackPanel.Children.Add(new Button());
			stackPanel.Children.Add(dockPanel);

			Assert.AreEqual(3, _workItem.Items.Count);
			Assert.IsTrue(_workItem.Items.ContainsObject(workspace1));
			Assert.IsTrue(_workItem.Items.ContainsObject(workspace2));
			_frameworkElementSmartPartStrategy.TearDown(context, stackPanel);
			Assert.AreEqual(1, _workItem.Items.Count);
			Assert.IsFalse(_workItem.Items.ContainsObject(workspace1));
			Assert.IsFalse(_workItem.Items.ContainsObject(workspace2));
		}

		[Test]
		public void TearDown_ShouldRecurseIntoChildrenOfRemovedItem()
		{
			//NOTE: I have my doubts about whether it is necessary for the children of a removed item to be considered for
			//removal. However, doing matches the behavior of the winforms layer

			IBuilderContext context = Mocks.DynamicMock<IBuilderContext>();
			IReadWriteLocator locator = Mocks.DynamicMultiMock<IReadWriteLocator>();
			Expect.Call(context.Locator).Repeat.Any().Return(locator);
			Expect.Call(locator.Get<WorkItem>(null)).IgnoreArguments().Repeat.Once().Return(_workItem);
			Mocks.ReplayAll();

			MockSmartPart smartPart1 = _workItem.Items.AddNew<MockSmartPart>();
			MockSmartPart smartPart2 = _workItem.Items.AddNew<MockSmartPart>();
			smartPart1.Content = smartPart2;

			DockPanel dockPanel = new DockPanel();
			dockPanel.Children.Add(smartPart1);

			Assert.AreEqual(3, _workItem.Items.Count);
			Assert.IsTrue(_workItem.Items.ContainsObject(smartPart1));
			Assert.IsTrue(_workItem.Items.ContainsObject(smartPart2));
			_frameworkElementSmartPartStrategy.TearDown(context, dockPanel);
			Assert.AreEqual(1, _workItem.Items.Count);
			Assert.IsFalse(_workItem.Items.ContainsObject(smartPart1));
			Assert.IsFalse(_workItem.Items.ContainsObject(smartPart2));
		}

		[Test]
		public void TearDown_ShouldNotRemoveRootItem()
		{
			IBuilderContext context = Mocks.DynamicMock<IBuilderContext>();
			IReadWriteLocator locator = Mocks.DynamicMultiMock<IReadWriteLocator>();
			Expect.Call(context.Locator).Repeat.Any().Return(locator);
			Expect.Call(locator.Get<WorkItem>(null)).IgnoreArguments().Repeat.Once().Return(_workItem);
			Mocks.ReplayAll();

			MockSmartPart smartPart1 = _workItem.Items.AddNew<MockSmartPart>();
			MockSmartPart smartPart2 = _workItem.Items.AddNew<MockSmartPart>();
			smartPart1.Content = smartPart2;

			Assert.AreEqual(3, _workItem.Items.Count);
			Assert.IsTrue(_workItem.Items.ContainsObject(smartPart1));
			Assert.IsTrue(_workItem.Items.ContainsObject(smartPart2));
			_frameworkElementSmartPartStrategy.TearDown(context, smartPart1);
			Assert.AreEqual(2, _workItem.Items.Count);
			Assert.IsTrue(_workItem.Items.ContainsObject(smartPart1));
			Assert.IsFalse(_workItem.Items.ContainsObject(smartPart2));
		}

		[Test]
		public void TearDown_ShouldWorkFromAnyThread()
		{
			IBuilderContext context = Mocks.DynamicMock<IBuilderContext>();
			IReadWriteLocator locator = Mocks.DynamicMultiMock<IReadWriteLocator>();
			Expect.Call(context.Locator).Repeat.Any().Return(locator);
			Expect.Call(locator.Get<WorkItem>(null)).IgnoreArguments().Repeat.Once().Return(_workItem);
			Mocks.ReplayAll();

			Button button = null;

			Thread thread = new Thread(delegate()
			{
				button = new Button();
			});

			thread.SetApartmentState(ApartmentState.STA);
			thread.Start();
			thread.Join();

			_frameworkElementSmartPartStrategy.TearDown(context, button);
		}

		#region Supporting Types

		[SmartPart]
		public class MockSmartPart : UserControl
		{
		}

		#endregion
	}
}
